from sqlalchemy import func, text
from sqlalchemy.orm import Session
from sqlalchemy.dialects.postgresql import array_agg
from server.app.cloud_ide.model import Ide, IdeEnvironment, IdeImage, IdeRegistry, IdeStackLink, IdeStack
from server.app.cloud_ide.schema.ide.request.ide_create_request import IdeCreateRequest
from server.app.cloud_ide.schema.ide.request.ide_update_request import IdeUpdateRequest
from server.framework.core.logger import logger
from server.framework.utils.keycloak_manager import build_keycloak_admin
from server.framework.utils.kubernetes.kubernetes_client import KubernetesClient


class IdeService:
    def find_ide_by_name(self, db: Session, name: str) -> Ide:
        entity = db.query(Ide).filter(Ide.name == name).first()
        return entity

    def get_ide_by_id(self, db: Session, id: str):
        entity = db.query(Ide.id, Ide.name, Ide.create_user_name, Ide.ingress_config,
                          Ide.volume_config, Ide.port_config,
                          Ide.ide_environment_id, Ide.status, Ide.created,
                          IdeEnvironment.name.label('ide_environment'),
                          IdeEnvironment.id.label('ide_environment_id'),
                          IdeImage.name.label('ide_image'),
                          IdeImage.id.label('ide_image_id'),
                          IdeRegistry.name.label('ide_registry'),
                          IdeRegistry.id.label('ide_registry_id')) \
            .join(IdeEnvironment, Ide.ide_environment_id == IdeEnvironment.id) \
            .join(IdeImage, Ide.ide_image_id == IdeImage.id) \
            .join(IdeRegistry, IdeImage.ide_registry_id == IdeRegistry.id) \
            .filter(id == Ide.id).one()
        return entity

    def get_ide_list(self, db: Session, user_id: str, tenant_id: str,
                     name: str, page_size: int, current_page: int, filter_by_user: bool):
        query = db.query(Ide.id, Ide.name, Ide.create_user_name,
                         text("coalesce(string_agg(ide_stack.id::text,','),'') as ide_stack_id"),
                         text("coalesce(string_agg(ide_stack.name::text,','),'') as ide_stack_name"),
                         Ide.ide_environment_id, Ide.status, Ide.created,
                         Ide.ingress_config, Ide.volume_config, Ide.port_config,
                         IdeEnvironment.name.label('ide_environment'),
                         IdeEnvironment.id.label('ide_environment_id'),
                         IdeImage.name.label('ide_image'),
                         IdeImage.id.label('ide_image_id'),
                         IdeImage.ide_type.label('ide_type'),
                         IdeRegistry.name.label('ide_registry'),
                         IdeRegistry.id.label('ide_registry_id')) \
            .join(IdeEnvironment, Ide.ide_environment_id == IdeEnvironment.id) \
            .join(IdeImage, Ide.ide_image_id == IdeImage.id) \
            .join(IdeRegistry, IdeImage.ide_registry_id == IdeRegistry.id) \
            .outerjoin(IdeStackLink, IdeStackLink.ide_id == Ide.id) \
            .outerjoin(IdeStack, IdeStack.id == IdeStackLink.ide_stack_id).distinct() \
            .group_by(
            Ide.id, Ide.name, Ide.create_user_name,
            Ide.ide_environment_id, Ide.status, Ide.created,
            Ide.ingress_config, Ide.volume_config, Ide.port_config, IdeEnvironment.name,
            IdeEnvironment.id, IdeImage.name, IdeImage.id, IdeImage.ide_type, IdeRegistry.name, IdeRegistry.id)
        if name is not None:
            query = query.filter(Ide.name.like("%" + name + "%"))
        query = query.filter(Ide.tenant_id == tenant_id)
        if filter_by_user:
            query = query.filter(Ide.create_user_id == user_id)
        query = query.order_by(Ide.created.desc())
        total = query.count()
        items = query.limit(page_size).offset((current_page - 1) * page_size).all()
        rs = []
        for item in items:
            dict_item = dict(item)
            dict_item['ide_stack_id'] = list(item)[3]
            dict_item['ide_stack_name'] = list(item)[4]
            rs.append(dict_item)
        return rs, total

    def stop_ide(self, db: Session, id: str, tenant_id: str) -> bool:
        try:
            keycloak_admin = build_keycloak_admin()
            group_details = keycloak_admin.get_group(group_id=tenant_id)
            namespace = group_details['attributes']['namespace'][0]
            kubernetes_manager = KubernetesClient(namespace=namespace)
            kubernetes_manager.delete_ide(id, False)

            ide = db.query(Ide).filter(Ide.id == id).one()
            ide.status = 3
            db.commit()
            return True
        except Exception as e:
            logger.error(e)
            return False

    def start_ide(self, db: Session, id: str, group_id: str) -> bool:
        try:
            ide = db.query(Ide).filter(Ide.id == id).one()
            ide_environment: IdeEnvironment = db.query(IdeEnvironment).filter(
                IdeEnvironment.id == ide.ide_environment_id).one()
            ide_image: IdeImage = db.query(IdeImage).filter(IdeImage.id == ide.ide_image_id).one()
            ide_registry: IdeRegistry = db.query(IdeRegistry).filter(IdeRegistry.id == ide_image.ide_registry_id).one()
            ide_stack = db.query(IdeStack) \
                .join(IdeStackLink).filter(IdeStackLink.ide_id == id).all()

            ide_type: str = ''
            if ide_image.ide_type == 0:
                ide_type = 'jupyter'
            if ide_image.ide_type == 1:
                ide_type = 'vscode'
            if ide_image.ide_type in [2, 3, 4, 5, 6]:
                ide_type = 'idea'

            if ide.port_config != '':
                kubernetes_client = KubernetesClient()
                node_ports = kubernetes_client.list_node_ports()
                spec_node_ports = set([x.split(':')[1] for x in ide.port_config.split(',')])
                if len(node_ports & spec_node_ports) != 0:
                    logger.info(f'存在重复的NodePort[{node_ports & spec_node_ports}]，开启失败')
                    return False

            dynamic_dict = {
                'id': ide.id,
                'storage_class': ide_environment.storage_class,
                'storage_size': str(ide_environment.max_disk),
                'node_ports': ide.port_config,
                'ide_type': ide_type,
                'volume_config': ide.volume_config,
                'node_name': '',
                'runtime': 'cpu',
                'ingress_configs': ide.ingress_config,
                'memory_limit': str(ide_environment.max_memory),
                'cpu_limit': str(ide_environment.max_cpu * 1000),
                'memory_request': '',
                'cpu_request': '',
                'image_name': ide_registry.registry + '/' + ide_image.name + ':' + ide_image.version,
                'ide_stack': ide_stack,
                # 'ide': ide,
                # 'ide_environment': ide_environment,
            }

            keycloak_admin = build_keycloak_admin()
            group_details = keycloak_admin.get_group(group_id=group_id)
            namespace = group_details['attributes']['namespace'][0]
            kubernetes_manager = KubernetesClient(namespace=namespace)
            kubernetes_manager.start_ide(dynamic_dict)
            ide.status = 1
            db.commit()
            return True
        except Exception as e:
            logger.error(e)
            return False

    def create_ide(self, db: Session, user_name: str, user_id: str, data: IdeCreateRequest) -> bool:
        try:
            entity = Ide()

            entity.name = data.name
            entity.tenant_id = data.tenant_id
            entity.port_config = data.port_config
            entity.volume_config = data.volume_config
            entity.ingress_config = data.ingress_config
            entity.status = data.status
            entity.ide_image_id = data.ide_image_id
            entity.ide_environment_id = data.ide_environment_id
            entity.create_user_name = user_name
            entity.create_user_id = user_id

            db.add(entity)

            if data.stack_ids:
                stack_ids = data.stack_ids.split(',')
                for stack in stack_ids:
                    ide_stack_link = IdeStackLink()
                    ide_stack_link.ide_stack_id = stack
                    ide_stack_link.ide_id = entity.id
                    db.add(ide_stack_link)
            db.commit()
            return True
        except Exception as e:
            logger.error(e)
            return False

    def delete_ide(self, db: Session, ids: list, group_id: str) -> bool:
        try:
            db.query(Ide).filter(Ide.id.in_(ids)).delete(synchronize_session=False)
            db.commit()

            keycloak_admin = build_keycloak_admin()
            group_details = keycloak_admin.get_group(group_id=group_id)
            namespace = group_details['attributes']['namespace'][0]
            kubernetes_manager = KubernetesClient(namespace=namespace)
            for id in ids:
                kubernetes_manager.delete_ide(id, True)
            return True
        except Exception as e:
            logger.error(e)
            return False

    def update_ide(self, db: Session, data: IdeUpdateRequest):
        try:
            entity: Ide = db.query(Ide).filter(Ide.id == data.id).first()

            entity.name = data.name
            entity.tenant_id = data.tenant_id
            entity.port_config = data.port_config
            entity.volume_config = data.volume_config
            entity.ingress_config = data.ingress_config
            entity.status = data.status
            entity.ide_image_id = data.ide_image_id
            entity.ide_environment_id = data.ide_environment_id

            db.query(IdeStackLink).filter(IdeStackLink.ide_id == data.id).delete()

            if data.stack_ids:
                stack_ids = data.stack_ids.split(',')
                for stack in stack_ids:
                    ide_stack_link = IdeStackLink()
                    ide_stack_link.ide_stack_id = stack
                    ide_stack_link.ide_id = entity.id
                    db.add(ide_stack_link)
            db.commit()
            return True
        except Exception as e:
            logger.error(e)
            return False
